<template>
  <div class="container">
    <!-- 游戏主界面 -->
    <div class="game-container" v-show="game.info.status !== 'wait'">
      <div class="info-area">
        <div class="round-info">
          <div class="round-cnt">第{{ game.info.round }}回合</div>
        </div>
        <div class="last-time">
          剩余{{ timer_cnt }}秒
        </div>
        <div class="last-card">
          本回合可出牌数：<span>{{ identity === PLAYER_ONE ? game.player1.allow : game.player2.allow }}</span>
        </div>
        <div
          class="last-card"
          v-show="(identity === PLAYER_ONE ? game.player1.status : game.player2.status) === 'wait'"
        >
          <div class="wait-info">等待对手中...</div>
        </div>
        <div class="result-area" v-show="resultBoard.visible">
          {{ resultBoard.text }}
        </div>
        <div
          class="btn-areas"
          v-show="(identity === PLAYER_ONE ? game.player1.status : game.player2.status) === 'gaming'"
        >
          <button class="over-btn" @click="overThisRound">结束本回合</button>
        </div>
        <div class="btn-areas" v-show="reloadBtn">
          <button class="over-btn" @click="reloadGame">再来一局</button>
        </div>
        <div class="change-area" @click="changeCard">
          <div class="inner">
            <div class="title">换牌</div>
            <div class="num">{{ identity === PLAYER_ONE ? game.player1.change : game.player2.change }}</div>
          </div>
        </div>
      </div>
      <div class="game-area">
        <div class="field-area" id="f1">
          <div class="his-cards">
            <div class="card-border">
              <CardItem 
                v-for="item in (identity === PLAYER_ONE ? game.info.field1.p2 : game.info.field1.p1)"
                :key="item.t + item.c"
                :info="item"
                :round="game.info.round"
                mode="his"
              />
            </div>
          </div>
          <div class="his-score">
            <ScoreItem
              :cards="[...(identity === PLAYER_ONE ? game.info.field1.p2 : game.info.field1.p1), ...game.info.field1.pre]"
              :effect="game.info.field1.effect"
              :pre="identity === PLAYER_ONE ? game.info.field1.p2s : game.info.field1.p1s"
              :round="game.info.round"
              :dec="identity === PLAYER_ONE ? bigger.f1.p2 : bigger.f1.p1"
              field="field1"
              mode="his"
            />
          </div>
          <div class="pre-cards">
            <CardItem 
              v-for="item in game.info.field1.pre"
              :key="item.t + item.c"
              :info="item"
            />
            <div class="pre-ps" v-show="game.info.field1.pre.length === 0">(暂无预设牌)</div>
          </div>
          <div class="field-info">
            <div class="field-text">{{ game.info.field1.effect.name }}</div>
          </div>
          <div class="my-score" @click="moveCard('field1')">
            <ScoreItem
              :cards="[...(identity === PLAYER_ONE ? game.info.field1.p1 : game.info.field1.p2), ...game.info.field1.pre]"
              :effect="game.info.field1.effect"
              :pre="identity === PLAYER_ONE ? game.info.field1.p1s : game.info.field1.p2s"
              :round="game.info.round"
              :dec="identity === PLAYER_ONE ? bigger.f1.p1 : bigger.f1.p2"
              field="field1"
              mode="my"
              ref="myScoreF1"
            />
          </div>
          <div class="my-cards" @click.self="moveCard('field1')">
            <div class="card-border" @click.self="moveCard('field1')">
              <CardItem 
                v-for="item in (identity === PLAYER_ONE ? game.info.field1.p1 : game.info.field1.p2)"
                :key="item.t + item.c"
                :info="item"
                :round="game.info.round"
                :cur-sel="cardSelected"
                @click="returnCard('field1', item)"
              />
              <div
                class="mycard-ps"
                v-show="(identity === PLAYER_ONE ? game.info.field1.p1 : game.info.field1.p2).length === 0"
                @click.self="moveCard('field1')"
              >
                请选择手牌并置入
              </div>
            </div>
          </div>
        </div>
        <div class="field-area" id="f2">
          <div class="his-cards">
            <div class="card-border">
              <CardItem 
                v-for="item in (identity === PLAYER_ONE ? game.info.field2.p2 : game.info.field2.p1)"
                :key="item.t + item.c"
                :info="item"
                :round="game.info.round"
                mode="his"
              />
            </div>
          </div>
          <div class="his-score">
            <ScoreItem
              :cards="[...(identity === PLAYER_ONE ? game.info.field2.p2 : game.info.field2.p1), ...game.info.field2.pre]"
              :effect="game.info.field2.effect"
              :pre="identity === PLAYER_ONE ? game.info.field2.p2s : game.info.field2.p1s"
              :round="game.info.round"
              :dec="identity === PLAYER_ONE ? bigger.f2.p2 : bigger.f2.p1"
              field="field2"
              mode="his"
            />
          </div>
          <div class="pre-cards">
            <CardItem
              v-for="item in game.info.field2.pre"
              :key="item.t + item.c"
              :info="item"
            />
            <div class="pre-ps" v-show="game.info.field2.pre.length === 0">(暂无预设牌)</div>
          </div>
          <div class="field-info">
            <div class="field-text">{{ game.info.field2.effect.name }}</div>
          </div>
          <div class="my-score" @click="moveCard('field2')">
            <ScoreItem
              :cards="[...(identity === PLAYER_ONE ? game.info.field2.p1 : game.info.field2.p2), ...game.info.field2.pre]"
              :effect="game.info.field2.effect"
              :pre="identity === PLAYER_ONE ? game.info.field2.p1s : game.info.field2.p2s"
              :round="game.info.round"
              :dec="identity === PLAYER_ONE ? bigger.f2.p1 : bigger.f2.p2"
              field="field2"
              mode="my"
              ref="myScoreF2"
            />
          </div>
          <div class="my-cards" @click.self="moveCard('field2')">
            <div class="card-border" @click.self="moveCard('field2')">
              <CardItem 
                v-for="item in (identity === PLAYER_ONE ? game.info.field2.p1 : game.info.field2.p2)"
                :key="item.t + item.c"
                :info="item"
                :round="game.info.round"
                :cur-sel="cardSelected"
                @click="returnCard('field2', item)"
              />
              <div
                class="mycard-ps"
                v-show="(identity === PLAYER_ONE ? game.info.field2.p1 : game.info.field2.p2).length === 0"
                @click.self="moveCard('field2')"
              >
                请选择手牌并置入
              </div>
            </div>
          </div>
        </div>
        <div class="field-area" id="f3">
          <div class="his-cards">
            <div class="card-border">
              <CardItem 
                v-for="item in (identity === PLAYER_ONE ? game.info.field3.p2 : game.info.field3.p1)"
                :key="item.t + item.c"
                :info="item"
                :round="game.info.round"
                mode="his"
              />
            </div>
          </div>
          <div class="his-score">
            <ScoreItem
              :cards="[...(identity === PLAYER_ONE ? game.info.field3.p2 : game.info.field3.p1), ...game.info.field3.pre]"
              :effect="game.info.field3.effect"
              :pre="identity === PLAYER_ONE ? game.info.field3.p2s : game.info.field3.p1s"
              :round="game.info.round"
              :dec="identity === PLAYER_ONE ? bigger.f3.p2 : bigger.f3.p1"
              field="field3"
              mode="his"
            />
          </div>
          <div class="pre-cards">
            <CardItem 
              v-for="item in game.info.field3.pre"
              :key="item.t + item.c"
              :info="item"
            />
            <div class="pre-ps" v-show="game.info.field3.pre.length === 0">(暂无预设牌)</div>
          </div>
          <div class="field-info">
            <div class="field-text">{{ game.info.field3.effect.name }}</div>
          </div>
          <div class="my-score" @click="moveCard('field3')">
            <ScoreItem
              :cards="[...(identity === PLAYER_ONE ? game.info.field3.p1 : game.info.field3.p2), ...game.info.field3.pre]"
              :effect="game.info.field3.effect"
              :pre="identity === PLAYER_ONE ? game.info.field3.p1s : game.info.field3.p2s"
              :round="game.info.round"
              :dec="identity === PLAYER_ONE ? bigger.f3.p1 : bigger.f3.p2"
              field="field3"
              mode="my"
              ref="myScoreF3"
            />
          </div>
          <div class="my-cards" @click.self="moveCard('field3')">
            <div class="card-border" @click.self="moveCard('field3')">
              <CardItem 
                v-for="item in (identity === PLAYER_ONE ? game.info.field3.p1 : game.info.field3.p2)"
                :key="item.t + item.c"
                :info="item"
                :round="game.info.round"
                :cur-sel="cardSelected"
                @click="returnCard('field3', item)"
              />
              <div
                class="mycard-ps"
                v-show="(identity === PLAYER_ONE ? game.info.field3.p1 : game.info.field3.p2).length === 0"
                @click.self="moveCard('field3')"
              >
                请选择手牌并置入
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="mycards-area">
        <div class="mycard-backg">
          <CardItem 
            v-for="item in (identity === PLAYER_ONE ? game.player1.card : game.player2.card)"
            :key="item.t + item.c"
            :info="item"
            :is-fold="true"
            :cur-sel="cardSelected"
            @click="selectCard(item)"
          />
        </div>
      </div>
      <div class="exit-game-area" @click="exitGame">←</div>
      <div class="teach-game-area" @click="teachDialog = !teachDialog">?</div>
      <div class="do-order-area" style="bottom: 110px; color: white; background-color: slateblue;" @click="cardTypeDialog = !cardTypeDialog">型</div>
      <div class="do-order-area" style="bottom: 76px; color: white; background-color: slategray;" @click="cardStorageDialog = !cardStorageDialog">堆</div>
      <div class="do-order-area" style="bottom: 42px; color: burlywood;" @click="orderCard_color">色</div>
      <div class="do-order-area" style="bottom: 8px; color: brown;" @click="orderCard_code">点</div>
    </div>
  </div>
  <div
    class="msg-box"
    v-show="messageBox.visible"
    :style="{ transform: messageBox.position }"
  >
    {{ messageBox.text }}
  </div>
  <!-- 游戏初始界面 -->
  <div class="prepare-container" v-show="game.info.status === 'wait'">
    <div class="game-title-show">
      <div class="title-game">亚兹牌</div>
      <div class="card1">
        <div class="ccd" style="color: orange;">K</div>
        <div class="cic" style="color: orange;">♦</div>
      </div>
      <div class="card2">
        <div class="ccd" style="color: blue;">A</div>
        <div class="cic" style="color: blue;">♠</div>
      </div>
    </div>
    <div class="player-info">
      {{ p1Text }} / {{ p2Text }}
    </div>
    <div class="prepare-btn">
      <button
        class="pbtn"
        :disabled="p1Text !== '玩家A' || p2Text !== '玩家B' || prepareBtnDis"
        @click="doPrepare"
        v-show="!gameStartBtn"
      >
        {{ prepareBtnText }}
      </button>
      <button
        class="srtn"
        @click="setWebsocket"
        v-show="gameStartBtn"
      >
        游戏开始
      </button>
    </div>
    <div class="first-btn" style="color: #e0950a; bottom: 87px;" @click="teachDialog = !teachDialog">教程</div>
    <div class="first-btn" style="color: #d038ac; bottom: 46px;" @click="aboutDialog = !aboutDialog">关于</div>
    <div class="first-btn" style="color: #5a2aff; bottom: 5px;" @click="settingDialog = !settingDialog">设置</div>
  </div>
  <Dialog
    :width="380"
    :height="280"
    :visible="cardTypeDialog"
    title="牌型说明"
    @close="cardTypeDialog = false"
  >
    <div class="ct-content" v-for="item in CARDTYPE_SHOW_AREA">
      <div class="ctitle" :style="{ color: item.color }">{{ item.name }}</div>
      <div class="cshow">
        <div class="cards" v-for="it in item.list" :style="{ color: it.c }">
          <div class="tt">{{ it.t }}</div>
          <div class="ic">{{ it.i }}</div>
        </div>
      </div>
      <div class="cscore" :style="{ color: item.color }">{{ item.score }}</div>
    </div>
  </Dialog>
  <Dialog
    :width="475"
    :height="280"
    :visible="cardStorageDialog"
    title="计牌器"
    @close="cardStorageDialog = false"
  >
    <div class="card-score-info">说明：点数2~10计2~10分；点数J、Q、K计11分；点数A计12分</div>
    <div class="cs-content">
      <div
        class="cs-card"
        v-for="item in allCardList_const"
        :style="{
          backgroundColor: getMineCards().includes(item) ? '#b9b9b9' : 'white',
          color: transCardColor_app(item.split('/')[1]),
        }"
      >
        <div class="ct">{{ item.split('/')[0] }}</div>
        <div class="ci">{{ transCardIcon_app(item.split('/')[1]) }}</div>
      </div>
    </div>
  </Dialog>
  <Dialog
    :width="380"
    :height="280"
    :visible="settingDialog"
    title="全局设置"
    @close="settingDialog = false"
  >
    <div class="set-intro">（为保证游戏体验，双方配置建议相同）</div>
    <div class="set-item">
      <input type="checkbox" v-model="PROFIX.IS_TIMER"/>回合时间限制
    </div>
    <div class="set-button">
      <div class="sbtn" @click="forceCloseWS" style="background-color: orangered;">一键强制重连</div>
    </div>
    <div class="set-button">
      <div class="sbtn" @click="resetWS" style="background-color: red;">重置后端</div>
    </div>
  </Dialog>
  <Dialog
    :width="380"
    :height="280"
    :visible="aboutDialog"
    title="关于"
    @close="aboutDialog = false"
  >
    <div class="about-para" v-for="p in ABOUT">{{ p }}</div>
  </Dialog>
  <Dialog
    :width="400"
    :height="280"
    :visible="teachDialog"
    title="教程"
    @close="teachDialog = false"
  >
    <TeachInfo></TeachInfo>
  </Dialog>
</template>

<script setup>
import { ref, reactive } from 'vue';
import { nanoid } from 'nanoid';
import { CONNECT_BASE_URL, ABOUT } from './constant';
import CardItem from './components/CardItem.vue';
import ScoreItem from './components/ScoreItem.vue';
import TeachInfo from './components/TeachInfo.vue';
import Dialog from './components/Dialog.vue';

// 配置模块
const UID = nanoid(); // 生成匿名玩家唯一ID
const identity = ref(''); // 玩家身份标记
const PLAYER_ONE = 'p1', PLAYER_TWO = 'p2';
const PROFIX = reactive({
  IS_LOG: false, // （开发者模式）控制台打印
  IS_TIMER: false, // 控制是否要计时
});
const CARDTYPE_SHOW_AREA = [
  {
    name: '同色序列', score: 120, color: '#ffb52d',
    list: [{t: '10', c: 'orange', i: '♦'},{t: 'J', c: 'orange', i: '♦'},{t: 'Q', c: 'orange', i: '♦'},{t: 'K', c: 'orange', i: '♦'},{t: 'A', c: 'orange', i: '♦'}]
  },
  {
    name: '四骑士', score: 80, color: '#bcd6ff',
    list: [{t: 'A', c: 'green', i: '♣'},{t: 'A', c: 'blue', i: '♠'},{t: 'A', c: 'orange', i: '♦'},{t: 'A', c: 'orange', i: '♦'}]
  },
  {
    name: '序列', score: 60, color: '#8effc7',
    list: [{t: '4', c: 'red', i: '♥'},{t: '5', c: 'red', i: '♥'},{t: '6', c: 'blue', i: '♠'},{t: '7', c: 'blue', i: '♠'},{t: '8', c: 'green', i: '♣'}]
  },
  {
    name: '同色', score: 60, color: '#aae59b',
    list: [{t: '2', c: 'green', i: '♣'},{t: 'K', c: 'green', i: '♣'},{t: '8', c: 'green', i: '♣'},{t: 'J', c: 'green', i: '♣'},{t: 'A', c: 'green', i: '♣'}]
  },
  {
    name: '满座', score: 55, color: '#ff8cf9',
    list: [{t: 'K', c: 'blue', i: '♠'},{t: 'K', c: 'orange', i: '♦'},{t: 'K', c: 'orange', i: '♦'},{t: '9', c: 'red', i: '♥'},{t: '9', c: 'green', i: '♣'}]
  },
  {
    name: '三贤者', score: 30, color: '#eef572',
    list: [{t: '5', c: 'red', i: '♥'},{t: '5', c: 'green', i: '♣'},{t: '5', c: 'blue', i: '♠'}]
  },
  {
    name: '双偶星', score: 30, color: '#ffa2a2',
    list: [{t: 'J', c: 'red', i: '♥'},{t: 'J', c: 'red', i: '♥'},{t: 'A', c: 'green', i: '♣'},{t: 'A', c: 'orange', i: '♦'}]
  },
  {
    name: '偶星', score: 15, color: '#ffdca2',
    list: [{t: '8', c: 'blue', i: '♠'},{t: '8', c: 'orange', i: '♦'}]
  },
  {
    name: '无牌型', score: 0, color: 'white',
    list: [{t: 'A', c: 'blue', i: '♠'}]
  }
];
const allCardList_const = [ // 牌池对照表
  'A/R', '2/R', '3/R', '4/R', '5/R', '6/R', '7/R', '8/R', '9/R', '10/R', 'J/R', 'Q/R', 'K/R',
  'A/G', '2/G', '3/G', '4/G', '5/G', '6/G', '7/G', '8/G', '9/G', '10/G', 'J/G', 'Q/G', 'K/G',
  'A/B', '2/B', '3/B', '4/B', '5/B', '6/B', '7/B', '8/B', '9/B', '10/B', 'J/B', 'Q/B', 'K/B',
  'A/O', '2/O', '3/O', '4/O', '5/O', '6/O', '7/O', '8/O', '9/O', '10/O', 'J/O', 'Q/O', 'K/O',
];

// 游戏准备模块
const prepareBtnDis = ref(false);
const prepareBtnText = ref('准备');
const p1Text = ref('等待中...');
const p2Text = ref('等待中...');
const doPrepare = () => {
  ws.send(JSON.stringify({ type: 'match', }));
};
const gameStartBtn = ref(true);
const settingDialog = ref(false);
const aboutDialog = ref(false);
const teachDialog = ref(false);

// 牌信息显示
const cardTypeDialog = ref(false); // 控制牌型展示框
const cardStorageDialog = ref(false); // 控制牌池展示框
const messageBox = reactive({ // 顶部提示框
  visible: false,
  text: 'none',
  position: 'translateX(-50%)',
});
const riseMsg = (content, time=1200, mid=false) => { // 开启一条全局通知
  messageBox.visible = true;
  messageBox.text = content;
  if(mid) messageBox.position = 'translateX(50%)';
  else messageBox.position = 'translateX(0)';
  setTimeout(() => {
    messageBox.visible = false;
  }, time);
};
const trashCards = reactive([]); // 记录换牌
const getMineCards = () => {
  const all = [];
  if(identity.value === PLAYER_ONE) { // p1
    all.push(
      ...game.player1.card, // 手牌
      ...game.info.field1.p1, // F1-p1
      ...game.info.field2.p1, // F2-p2
      ...game.info.field3.p1, // F3-p3
      ...game.info.field1.pre, // F1-pre
      ...game.info.field2.pre, // F2-pre
      ...game.info.field3.pre, // F3-pre
      ...trashCards,
    );
  }
  if(identity.value === PLAYER_TWO) { // p2
    all.push(
      ...game.player2.card,
      ...game.info.field1.p2,
      ...game.info.field2.p2,
      ...game.info.field3.p2,
      ...game.info.field1.pre,
      ...game.info.field2.pre,
      ...game.info.field3.pre,
      ...trashCards,
    );
  }
  return all.map(item => item.t + '/' + item.c);
};
const transCardColor_app = (clr) => {
  switch(clr) {
    case 'R': return 'red';
    case 'G': return 'green';
    case 'B': return 'blue';
    case 'O': return 'orange';
    default: return 'grey'; 
  }
};
const transCardIcon_app = (clr) => {
  switch(clr) {
    case 'R': return '♥';
    case 'G': return '♣';
    case 'B': return '♠';
    case 'O': return '♦';
    default: return '●'; 
  }
};

// 回合计时
let timer = null;
let timer_cnt = ref(0);

// 对局信息对象
const game = reactive({
  info: {
    status: 'wait',
    round: 0,
    cards: [],
    field1: { pre: [], effect: { id: -1, name: '无效果', extra: '' }, p1: [], p2: [], p1s: [], p2s: [] },
    field2: { pre: [], effect: { id: -1, name: '无效果', extra: '' }, p1: [], p2: [], p1s: [], p2s: [] },
    field3: { pre: [], effect: { id: -1, name: '无效果', extra: '' }, p1: [], p2: [], p1s: [], p2s: [] },
  },
  player1: {
    change: 0,
    card: [],
  },
  player2: {
    change: 0,
    card: [],
  },
});

// 我的分数信息
const myScoreF1 = ref(null);
const myScoreF2 = ref(null);
const myScoreF3 = ref(null);
const bigger = reactive({
  f1: { p1: false, p2: false },
  f2: { p1: false, p2: false },
  f3: { p1: false, p2: false }
}); // 结算时圈出更高分数

// 游戏操作模块
const cardSelected = ref(null); // 选择的卡牌
const overCheck = () => { // 当前回合结束无法操作检查
  if(identity.value === PLAYER_ONE && game.player1.status === 'wait') return true;
  if(identity.value === PLAYER_TWO && game.player2.status === 'wait') return true;
  return false;
};
const orderCard_code = () => { // 手牌排序：code点数
  if(identity.value === PLAYER_ONE) { // p1
    const tempList = game.player1.card.map(item => {
      let t_n = 0;
      if(item.t === 'J') t_n = 11;
      else if(item.t === 'Q') t_n = 12;
      else if(item.t === 'K') t_n = 13;
      else if(item.t === 'A') t_n = 14;
      else t_n = Number(item.t);
      return { ...item, t_n };
    });
    tempList.sort((a, b) => b.t_n - a.t_n);
    game.player1.card = tempList;
  } else { // p2
    const tempList = game.player2.card.map(item => {
      let t_n = 0;
      if(item.t === 'J') t_n = 11;
      else if(item.t === 'Q') t_n = 12;
      else if(item.t === 'K') t_n = 13;
      else if(item.t === 'A') t_n = 14;
      else t_n = Number(item.t);
      return { ...item, t_n };
    });
    tempList.sort((a, b) => b.t_n - a.t_n);
    game.player2.card = tempList;
  }
};
const orderCard_color = () => { // 手牌排序：color花色
  if(identity.value === PLAYER_ONE) { // p1
    const tempList = [[], [], [], []];
    game.player1.card.forEach(item => {
      if(item.c === 'R') tempList[0].push(item);
      if(item.c === 'G') tempList[1].push(item);
      if(item.c === 'B') tempList[2].push(item);
      if(item.c === 'O') tempList[3].push(item);
    });
    game.player1.card = [ ...tempList[0], ...tempList[1], ...tempList[2], ...tempList[3] ];
  } else { // p2
    const tempList = [[], [], [], []];
    game.player2.card.forEach(item => {
      if(item.c === 'R') tempList[0].push(item);
      if(item.c === 'G') tempList[1].push(item);
      if(item.c === 'B') tempList[2].push(item);
      if(item.c === 'O') tempList[3].push(item);
    });
    game.player2.card = [ ...tempList[0], ...tempList[1], ...tempList[2], ...tempList[3] ];
  }
};
const selectCard = (card) => { // 选择卡牌
  if(overCheck()) {
    riseMsg('当前回合已结束', 1500);
    return ;
  }
  if(cardSelected.value === null) {
    if(PROFIX.IS_LOG) { console.log('选择卡牌', card); }
    cardSelected.value = card;
  } else {
    if(PROFIX.IS_LOG) {
      console.log('取消选择', cardSelected.value);
      console.log('选择卡牌', card);
    }
    cardSelected.value = card;
  }
};
const moveCard = (field) => { // 把手牌置于场上某一区域
  if(overCheck()) {
    riseMsg('当前回合已结束', 1500);
    return ;
  }
  if(cardSelected.value === null) {
    riseMsg('请选择一张牌');
    return ;
  } ;
  if(identity.value === PLAYER_ONE) {
    // 统计场上有几张当前回合的牌
    const curFieldCards = [ ...game.info.field1.p1, ...game.info.field2.p1, ...game.info.field3.p1 ];
    const cnt = curFieldCards.reduce((pre, cur) => pre + (cur.r === game.info.round ? 1 : 0), 0);
    // 每回合出牌有限制
    if(cnt === game.player1.allow) {
      riseMsg('本回合最多出' + game.player1.allow + '张牌');
      return ;
    }
    // 每个区域最多放四张牌
    if(game.info[field].p1.length === 4) {
      riseMsg('每个区域最多放四张牌');
      return ;
    }
  }
  if(identity.value === PLAYER_TWO) {
    const curFieldCards = [ ...game.info.field1.p2, ...game.info.field2.p2, ...game.info.field3.p2 ];
    const cnt = curFieldCards.reduce((pre, cur) => pre + (cur.r === game.info.round ? 1 : 0), 0);
    if(cnt === game.player2.allow) {
      riseMsg('本回合最多出' + game.player2.allow + '张牌');
      return ;
    }
    if(game.info[field].p2.length === 4) {
      riseMsg('每个区域最多放四张牌');
      return ;
    }
  }
  if(PROFIX.IS_LOG) { console.log(`牌移动到${field}`, cardSelected.value); }
  ws.send(JSON.stringify({
    type: 'move',
    to: field,
    identity: identity.value,
    card: cardSelected.value,
  }));
  cardSelected.value = null;
};
const returnCard = (field, card) => { // 将场上的牌撤回
  if(overCheck()) {
    riseMsg('当前回合已结束', 1500);
    return ;
  }
  // 被撤牌不属于本回合，无法操作
  if(card.r !== game.info.round) {
    riseMsg('仅能撤走本回合的出牌')
    return ;
  } ;
  if(PROFIX.IS_LOG) { console.log('撤回牌', card); }
  ws.send(JSON.stringify({
    type: 'return',
    from: field,
    identity: identity.value,
    card,
  }));
  cardSelected.value = null;
};
const changeCard = () => { // 换牌
  if(overCheck()) {
    riseMsg('当前回合已结束', 1500);
    return ;
  }
  if(cardSelected.value === null) return ;
  // 还剩换牌次数
  if(
    (identity.value === PLAYER_ONE && game.player1.change !== 0) ||
    (identity.value === PLAYER_TWO && game.player2.change !== 0)
  ) {
    if(PROFIX.IS_LOG) { console.log('换牌', cardSelected.value); }
    trashCards.push(cardSelected.value); // 记录换牌
    ws.send(JSON.stringify({
      type: 'change',
      identity: identity.value,
      card: cardSelected.value,
    }));
    cardSelected.value = null;
  } else {
    riseMsg('换牌次数已用完');
    return ;
  }
};
const overThisRound = () => { // 结束本回合
  if(overCheck()) return ;
  if(PROFIX.IS_LOG) { console.log(`结束第${game.info.round}回合`); }
  // 重置回合计时
  if(PROFIX.IS_TIMER) {
    clearInterval(timer);
    timer = null;
    timer_cnt.value = 0;
  }
  ws.send(JSON.stringify({
    type: 'over',
    identity: identity.value,
    round: game.info.round,
    score: [ myScoreF1.value.score, myScoreF2.value.score, myScoreF3.value.score ],
  }));
  cardSelected.value = null;
};
const reloadBtn = ref(false);
const resultBoard = reactive({
  visible: false,
  text: '',
});
const reloadGame = () => { // 再来一局
  forceCloseWS();
};
const exitGame = () => {
  if(confirm('确认退出游戏？')) {
    forceCloseWS();
  }
}

// WebSocket连接模块
let ws = null;
let lockReconnect = false; // websocket连接状态锁，避免重复连接
let forceDisconnect = false; // 强制断开连接
function setWebsocket() {
  gameStartBtn.value = false;
  ws = new WebSocket(`ws://${CONNECT_BASE_URL}/load`);
  ws.onopen = function(event) {
    if(PROFIX.IS_LOG) { console.log('@WS连接已建立'); }
    ws.send(JSON.stringify({
      type: 'connect',
      uid: UID,
    }));
  }
  ws.onclose = function(event) {
    if(PROFIX.IS_LOG) { console.log("@WS断连"); }
    if(!forceDisconnect) reconnect();
  }
  ws.onerror = function(event) {
    if(PROFIX.IS_LOG) { console.log("@WS出错"); }
    if(!forceDisconnect) reconnect();
  }
  ws.onmessage = function(wsres) {
    let answer = undefined;
    try { answer = JSON.parse(wsres.data); }
    catch { answer = {}; }
    switch(answer.type) {
      case 'connect': { // 连接系统
        if(PROFIX.IS_LOG) { console.log('@WS连接结果', answer); }
        if(answer.status === PLAYER_ONE) {
          identity.value = answer.status;
          p1Text.value = '玩家A';
          break;
        }
        if(answer.status === PLAYER_TWO) {
          identity.value = answer.status;
          p2Text.value = '玩家B';
          break;
        }
        if(answer.status === 'prepare') {
          if(PROFIX.IS_LOG) { console.log('请准备', identity.value); }
          p1Text.value = '玩家A';
          p2Text.value = '玩家B';
          break;
        }
        riseMsg('用户已满或连接失败');
        if(PROFIX.IS_LOG) { console.log('用户已满或连接失败'); }
        break;
      };
      case 'match': { // 准备结果
        if(answer.status === 'success') {
          prepareBtnDis.value = true;
          prepareBtnText.value = '已准备';
        }
        break;
      };
      case 'game': { // 获取对局信息
        if(PROFIX.IS_LOG) { console.log('对局信息更新', answer); }
        game.info = answer.game;
        game.player1 = answer.player1;
        game.player2 = answer.player2;
        // 初始化回合计时器
        if(
          PROFIX.IS_TIMER &&
          timer === null &&
          ((identity.value === 'p1' && game.player1.status !== 'wait') ||
           (identity.value === 'p2' && game.player2.status !== 'wait'))
        ) {
          timer_cnt.value = game.info.time;
          timer = setInterval(() => {
            timer_cnt.value--;
            if(timer_cnt.value === 0) overThisRound();
          }, 1000);
        }
        if(game.info.status === 'finish') {
          if(PROFIX.IS_LOG) { console.log('游戏结束'); }
          // 结算逻辑
          let p1w = 0, p2w = 0;
          reloadBtn.value = true;
          if(game.info.field1.p1s[3].total > game.info.field1.p2s[3].total) {
            bigger.f1.p1 = true;
            p1w++;
          } else if(game.info.field1.p1s[3].total < game.info.field1.p2s[3].total) {
            bigger.f1.p2 = true;
            p2w++;
          }
          if(game.info.field2.p1s[3].total > game.info.field2.p2s[3].total) {
            bigger.f2.p1 = true;
            p1w++;
          } else if(game.info.field2.p1s[3].total < game.info.field2.p2s[3].total) {
            bigger.f2.p2 = true;
            p2w++;
          }
          if(game.info.field3.p1s[3].total > game.info.field3.p2s[3].total) {
            bigger.f3.p1 = true;
            p1w++;
          } else if(game.info.field3.p1s[3].total < game.info.field3.p2s[3].total) {
            bigger.f3.p2 = true;
            p2w++;
          }
          if(PROFIX.IS_LOG) { console.log(`分数比较结果：p1胜${p1w}次，p2胜${p2w}次`); }
          resultBoard.visible = true;
          if(p1w > p2w) {
            resultBoard.text = identity.value === PLAYER_ONE ? '本局胜利' : '本局失败';
          } else if(p1w < p2w) {
            resultBoard.text = identity.value === PLAYER_TWO ? '本局胜利' : '本局失败';
          } else {
            resultBoard.text = '双方平局';
          }
        }
        break;
      };
    }
  }
}
function reconnect() {
  // 加锁是为了保证每次重连只有一个定时器在运行，否则会创建多个
  if(lockReconnect) return ;
  lockReconnect = true;
  setTimeout(function () {
    setWebsocket();
    lockReconnect = false;
  }, 2000); // 断开后2s自动重连
}
function forceCloseWS() { // 强制断连
  if(ws === null) {
    window.location.reload();
    return ;
  }
  ws.close();
  forceDisconnect = true;
  window.location.reload();
}
function resetWS() { // 重置后端
  if(ws === null) {
    riseMsg('还未连接', 1000, true);
    return ;
  }
  ws.send(JSON.stringify({
    type: 'resetclient',
  }));
  riseMsg('已重置', 1000, true);
}

</script>

<style lang="scss" scoped>
.container {
  width: 100vw;
  height: 100vh;
  overflow: hidden;
  background: linear-gradient(to bottom, rgb(0, 117, 59), rgb(0, 34, 169));
}
.game-container {
  width: 90vw;
  height: 100vh;
  overflow: hidden;
}
.do-order-area {
  position: fixed;
  left: 5px;
  width: 30px;
  line-height: 30px;
  border-radius: 50%;
  background-color: white;
  box-shadow: 0 0 1vh #ddd;
  text-align: center;
  font-size: 12px;
  cursor: pointer;
}
.exit-game-area {
  position: fixed;
  top: 5px;
  left: 5px;
  width: 25px;
  line-height: 25px;
  border-radius: 50%;
  background-color: white;
  box-shadow: 0 0 1vh #ddd;
  text-align: center;
  font-size: 12px;
  color: red;
  cursor: pointer;
}
.teach-game-area {
  position: fixed;
  top: 35px;
  left: 5px;
  width: 25px;
  line-height: 25px;
  border-radius: 50%;
  background-color: white;
  box-shadow: 0 0 1vh #ddd;
  text-align: center;
  font-size: 12px;
  color: grey;
  cursor: pointer;
}
.info-area {
  width: 10vw;
  height: 100vh;
  position: fixed;
  right: 0;
  top: 0;
  background-color: rgba(255, 255, 255, 0.7);
  border-top-left-radius: 10px;
  border-bottom-left-radius: 10px;
  box-shadow: 0 0 2vh rgb(234, 255, 203);
  .round-info {
    width: 100%;
    display: flex;
    justify-content: center;
    .round-cnt {
      margin: 7px 0 5px 0;
      padding: 0 5px 0 5px;
      font-size: 14px;
      font-weight: bold;
      line-height: 25px;
      color: rgb(0, 83, 0);
      text-align: center;
      background-color: white;
      border-radius: 10px;
    }
  }
  .last-time {
    width: 100%;
    line-height: 20px;
    text-align: center;
    font-size: 12px;
    color: #5a2aff
  }
  .last-card {
    width: 100%;
    line-height: 20px;
    text-align: center;
    font-size: 12px;
    color: rgb(0, 83, 31);
    span {
      display: inline-block;
      padding: 0 3px 0 3px;
      border-radius: 3px;
      font-weight: bold;
      background-color: white;
    }
  }
  .wait-info {
    width: 80%;
    margin: auto;
    color: purple;
    font-size: 12px;
    border-radius: 8px;
    margin-top: 5px;
    background-color: white;
  }
  .result-area {
    width: 100%;
    line-height: 20px;
    text-align: center;
    font-size: 13px;
    color: rgb(255, 50, 95);
    font-weight: bold;
  }
  .btn-areas {
    width: 100%;
    display: flex;
    justify-content: center;
    margin-top: 15px;
    .over-btn {
      width: 80%;
      font-size: 13px;
      color: white;
      background-color: rgb(255, 174, 54);
      border-radius: 10px;
      border: 1px solid rgb(212, 78, 0);
      box-shadow: 0 0 2vh white;
      cursor: pointer;
    }
  }
  .change-area {
    width: 100%;
    height: 60px;
    position: absolute;
    bottom: 5px;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    .inner {
      width: 80%;
      height: 48px;
      border-top-left-radius: 12px;
      border-bottom-right-radius: 12px;
      background-color: rgb(236, 189, 189);
      border-left: 1.5px solid red;
      border-right: 1.5px solid blue;
      border-top: 1.5px solid orange;
      border-bottom: 1.5px solid green;
      box-shadow: 0 0 2vh white;
      .title {
        line-height: 25px;
        text-align: center;
        font-size: 14px;
        color: white;
      }
      .num {
        line-height: 25px;
        text-align: center;
        font-size: 17px;
        color: white;
      }
    }
  }
}
.game-area {
  width: 100%;
  height: 80vh;
  display: flex;
}
.field-area {
  flex: 1;
  height: 100%;
  .card-border {
    width: fit-content;
    min-width: 60px;
    height: 85%;
    max-height: 100px;
    padding-right: 5px;
    padding-left: 25px;
    border: 1px solid #ddd;
    border-radius: 10px;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 
      0 0 2px rgba(193, 193, 193, 0.9),
      0 0 4px rgba(193, 193, 193, 0.6),
      0 0 6px rgba(193, 193, 193, 0.4),
      0 0 8px rgba(193, 193, 193, 0.2);
  }
  .mycard-ps {
    font-size: 12px;
    color: rgb(151, 151, 151);
    margin-left: -15px;
    cursor: pointer;
  }
  .his-cards {
    width: 100%;
    height: 19%;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .his-score {
    width: 100%;
    height: 13%;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .pre-cards {
    width: 100%;
    height: 21%;
    display: flex;
    align-items: center;
    justify-content: center;
    .pre-ps {
      font-size: 12px;
      color: rgb(227, 227, 227);
    }
  }
  .field-info {
    width: 100%;
    height: 10%;
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: auto;
    .field-text {
      width: fit-content;
      line-height: 20px;
      text-align: center;
      padding: 0 7px 0 7px;
      background-color: #dddddd37;
      border-radius: 5px;
      font-weight: bold;
      font-size: 12px;
      color: rgb(247, 195, 230);
      user-select: none;
    }
  }
  .my-score {
    width: 100%;
    height: 13%;
    display: flex;
    justify-content: center;
    align-items: center;
  }
  .my-cards {
    width: 100%;
    height: 24%;
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
  }
}
.mycards-area {
  width: 100%;
  height: 20vh;
  display: flex;
  align-items: center;
  justify-content: center;
  .mycard-backg {
    width: 95%;
    height: 95%;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: #dddddd2c;
    border-radius: 10px;
  }
}
.prepare-container {
  width: 360px;
  height: 180px;
  background-color: rgba(255, 255, 255, 0.7);
  box-shadow: 0 0 2vh #b9b9b9;
  border-radius: 15px;
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translateX(-50%) translateY(-30%);
  display: flex;
  flex-wrap: wrap;
  .game-title-show {
    width: 300px;
    height: 75px;
    position: absolute;
    top: -70px;
    left: 50%;
    transform: translateX(-50%);
    .title-game {
      width: 170px;
      line-height: 30px;
      font-size: 30px;
      font-weight: bold;
      text-align: center;
      color: #67c7c7a7;
      position: absolute;
      bottom: 20px;
      left: 50%;
      z-index: 52;
      transform: translateX(-50%);
    }
    .card1 {
      width: 60px;
      height: 75px;
      background-color: white;
      box-shadow: 0 0 1vh #ddd;
      border-radius: 6px;
      position: absolute;
      bottom: 0px;
      left: 100px;
      z-index: 50;
      transform: rotateZ(-30deg);
    }
    .card2 {
      width: 60px;
      height: 75px;
      background-color: white;
      box-shadow: 0 0 1vh #ddd;
      border-radius: 6px;
      position: absolute;
      bottom: 0px;
      right: 100px;
      z-index: 51;
      transform: rotateZ(20deg);
    }
    .ccd {
      width: 55px;
      height: 35px;
      padding-left: 5px;
      margin-top: 3px;
      font-size: 15px;
      font-weight: bold;
    }
    .cic {
      width: 60px;
      line-height: 10px;
      text-align: center;
      font-size: 17px;
      font-weight: bold;
    }
  }
  .player-info {
    width: 360px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 28px;
    color: rgb(0, 61, 0);
    font-weight: bold;
  }
  .prepare-btn {
    width: 360px;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .first-btn {
    width: 35px;
    line-height: 35px;
    border-radius: 50%;
    background-color: white;
    box-shadow: 0 0 1vh #ddd;
    font-size: 13px;
    text-align: center;
    position: absolute;
    right: 5px;
    cursor: pointer;
  }
  .pbtn {
    width: 80px;
    height: 45px;
    font-size: 21px;
    color: white;
    border: none;
    box-shadow: 0 0 1vh rgb(218, 255, 218);
    background-color: rgb(106, 215, 106);
    border-radius: 30px;
    cursor: pointer;
  }
  .pbtn:disabled {
    cursor: not-allowed;
    background-color: rgb(220, 255, 220);
    border-radius: 30px;
  }
  .srtn {
    width: 100px;
    height: 45px;
    font-size: 19px;
    color: white;
    border: none;
    box-shadow: 0 0 1vh rgb(192, 243, 255);
    background-color: rgb(71, 114, 255);
    border-radius: 30px;
    cursor: pointer;
  }
}
.msg-box {
  position: fixed;
  top: 10px;
  right: 50%;
  line-height: 22px;
  padding: 2px 6px 2px 6px;
  font-size: 13px;
  font-weight: bold;
  color: rgb(210, 41, 41);
  border: 1px solid rgb(180, 180, 180);
  background-color: rgba(255, 255, 255, 0.85);
  border-radius: 5px;
  z-index: 102;
}
.ct-content {
  width: 380px;
  margin-top: 5px;
  height: 60px;
  background-color: #66dfdf;
  display: flex;
  border-radius: 10px;
  overflow: hidden;
  margin-bottom: 8px;
  .ctitle {
    width: 85px;
    line-height: 60px;
    background-color: #379898;
    text-align: center;
    font-size: 17px;
    font-weight: bold;
  }
  .cshow {
    width: 235px;
    height: 60px;
    display: flex;
    align-items: center;
    justify-content: center;
    .cards {
      width: 40px;
      height: 52px;
      background-color: white;
      box-shadow: 0 0 1vh #c7c7c7;
      border-radius: 6px;
      margin-left: -10px;
      .tt {
        width: 35px;
        margin-left: 5px;
        line-height: 20px;
        font-size: 14px;
        font-weight: bold;
      }
      .ic {
        width: 40px;
        line-height: 20px;
        font-size: 16px;
        text-align: center;
        font-weight: bold;
      }
    }
  }
  .cscore {
    width: 60px;
    line-height: 60px;
    background-color: #14d2d2;
    text-align: center;
    font-size: 18px;
    font-weight: bold;
  }
}
.cs-content {
  width: 100%;
  padding-left: 15px;
  display: flex;
  flex-wrap: wrap;
}
.card-score-info {
  margin-top: 5px;
  margin-left: 5px;
  font-size: 13px;
  font-weight: bold;
  color: #d88900;
}
.cs-card {
  width: 45px;
  height: 50px;
  margin-left: -10px;
  margin-top: 10px;
  border-radius: 5px;
  box-shadow: 0 0 1vh #ddd;
  font-weight: bold;
  .ct {
    margin-top: 1px;
    margin-left: 5px;
  }
  .ci {
    margin-left: 10px;
  }
}
.set-intro {
  color: rgb(145, 145, 145);
  font-size: 13px;
  width: 100%;
  line-height: 20px;
  text-align: center;
  user-select: none;
}
.set-button {
  width: 100%;
  height: 35px;
  display: flex;
  align-items: center;
  .sbtn {
    color: rgb(255, 255, 255);
    font-size: 14px;
    width: fit-content;
    line-height: 24px;
    padding: 0 6px 0 6px;
    border-radius: 6px;
    margin-left: 5px;
    cursor: pointer;
  }
  .sbtn:hover {
    box-shadow: 0 0 5px #a4a4a4;
  }
}
.set-item {
  color: rgb(55, 55, 55);
  font-size: 14px;
  width: 100%;
  height: 25px;
  display: flex;
  align-items: center;
}
.about-para {
  line-height: 20px;
  font-size: 14px;
  color: rgb(62, 62, 62);
}
</style>
